CARGO_COMPILE_COMMANDS = $(BINDIR)/cargo-compile-commands.json
CARGO_COMPILE_COMMANDS_FLAGS = --clang

# When an application crate is built, it has to use rust_riotmodules_standalone itself to
# pull in any Rust modules that might be enabled through RIOT's module system.
# (If the application fails to add the rust_riotmodules_standalone dependency, that will
# go unnoticed and work fine if none of the catchall-dispatched modules are
# active -- but if one is enabled, this also serves to ensure the application
# is not silently built without them, as the feature will not be available to
# Cargo).
#
# This list should eventually be autogenerated.
ifneq (,$(filter lsm303agr,$(USEMODULE)))
  CARGO_OPTIONS += --features rust_riotmodules/riot-module-lsm303agr
endif
ifneq (,$(filter shell_democommands,$(USEMODULE)))
  CARGO_OPTIONS += --features rust_riotmodules/riot-module-shell-democommands
endif

# This is duplicating the compile-commands rule because unlike in the use case
# when a $(RIOTBASE)/compile_commands.json is built, we *want* this to be
# per-board and per-application. (The large mechanisms are shared anyway).
#
# Changes relative to the compile-commands rule: This uses lazysponge to keep
# Rust from rebuilding, and uses a custom output file and
# CARGO_COMPILE_COMMAND_FLAGS.
$(CARGO_COMPILE_COMMANDS): $(BUILDDEPS)
	$(Q)DIRS="$(DIRS)" APPLICATION_BLOBS="$(BLOBS)" \
	  "$(MAKE)" -C $(APPDIR) -f $(RIOTMAKE)/application.inc.mk compile-commands
	@# replacement addresses https://github.com/rust-lang/rust-bindgen/issues/1555
	@# Keep triples in sync with makefiles/arch/riscv.inc.mk
	$(Q)$(RIOTTOOLS)/compile_commands/compile_commands.py $(CARGO_COMPILE_COMMANDS_FLAGS) $(BINDIR) \
	  | sed -e 's/"riscv32-none-elf"/"riscv32"/g' \
	        -e 's/"riscv-none-elf"/"riscv32"/g' \
	        -e 's/"riscv32-unknown-elf"/"riscv32"/g' \
	        -e 's/"riscv32-elf"/"riscv32"/g' \
	        -e 's/"riscv64-none-elf"/"riscv32"/g' \
	        -e 's/"riscv64-unknown-elf"/"riscv32"/g' \
	        -e 's/"riscv64-elf"/"riscv32"/g' \
	  | $(LAZYSPONGE) $@

cargo-preflight: FORCE
	@command -v cargo >/dev/null || ($(COLOR_ECHO) \
		'$(COLOR_RED)Error: `cargo` command missing to build Rust modules.$(COLOR_RESET) Please install as described on <https://guide.riot-os.org/rust_tutorials/rust_in_riot/>.' ;\
		exit 1)
	@command -v $${C2RUST:-c2rust} >/dev/null || ($(COLOR_ECHO) \
		'$(COLOR_RED)Error: `'$${C2RUST:-c2rust}'` command missing to build Rust modules.$(COLOR_RESET) Please install as described on <https://guide.riot-os.org/rust_tutorials/rust_in_riot/>.' ;\
		exit 1)
	@command -v rustup >/dev/null || ($(COLOR_ECHO) \
		'$(COLOR_RED)Error: `rustup` command missing.$(COLOR_RESET) While it is not essential for building Rust modules, it is the only known way to install the target core libraries (or nightly for -Zbuild-std) needed to do so. If you do think that building should be possible, please edit this file, and file an issue about building Rust modules with the installation method you are using -- later checks in this file, based on rustup, will need to be adjusted for that.' ;\
		exit 1)
	@[ x"${RUST_TARGET}" != x"" ] || ($(COLOR_ECHO) "$(COLOR_RED)Error: No RUST_TARGET was set for this platform.$(COLOR_RESET) Set FEATURES_REQUIRED+=rust_target to catch this earlier."; exit 1)
	@# If distribution installed cargos ever grow the capacity to build RIOT, this absence of `rustup` might be OK. But that'd need them to both have cross tools around and cross core libs, none of which is currently the case.
	@# Ad grepping for "std": We're not *actually* checking for std but more for core -- but rust-stc-$TARGET is the name of any standard libraries that'd be available for that target.
	@[ x"$(findstring build-std,$(CARGO_OPTIONS))" != x"" ] || \
		(rustup component list --installed | grep 'rust-std-$(RUST_TARGET)$$' -q) || \
		($(COLOR_ECHO) \
		'$(COLOR_RED)Error: No Rust libraries are installed for the board'"'"'s CPU.$(COLOR_RESET) Run\n    $(COLOR_GREEN)$$$(COLOR_RESET) rustup target add $(RUST_TARGET)\nor set `CARGO_OPTIONS=-Zbuild-std=core`.'; \
		exit 1)

$(CARGO_LIB): cargo-preflight $(RIOTBUILD_CONFIG_HEADER_C) $(BUILDDEPS) $(CARGO_COMPILE_COMMANDS) FORCE
	@# mind the "+" to pass down make's jobserver.
	$(Q)+ CC= CFLAGS= CPPFLAGS= CXXFLAGS= \
		RIOT_COMPILE_COMMANDS_JSON="$(CARGO_COMPILE_COMMANDS)" \
		CARGO_BUILD_TARGET="$(RUST_TARGET)" \
		cargo \
			build \
			--profile $(CARGO_PROFILE) \
			$(CARGO_OPTIONS)

cargo-command: cargo-preflight $(RIOTBUILD_CONFIG_HEADER_C) $(CARGO_COMPILE_COMMANDS) FORCE
	@[ x"$(CARGO_COMMAND)" != x"" ] || ($(COLOR_ECHO) "$(COLOR_RED)Error: Running cargo-command requires a CARGO_COMMAND to be set.$(COLOR_RESET) Set CARGO_COMMAND=\"cargo clippy --release --fix\" or any other cargo command to run with the right RIOT environment."; exit 1)
	@# mind the "+" to pass down make's jobserver.
	$(Q)+ CC= CFLAGS= CPPFLAGS= CXXFLAGS= \
		RIOT_COMPILE_COMMANDS_JSON="$(CARGO_COMPILE_COMMANDS)" \
		CARGO_BUILD_TARGET="$(RUST_TARGET)" \
		PROFILE="$(CARGO_PROFILE)" \
		$(CARGO_COMMAND)

$(APPLICATION_RUST_MODULE).module: $(CARGO_LIB) FORCE
	$(Q)# Ensure no old object files persist. These would lead to duplicate
	$(Q)# symbols, or worse, lingering behaivor of XFA entries.
	$(Q)rm -rf $(BINDIR)/$(APPLICATION_RUST_MODULE)/
	$(Q)mkdir -p $(BINDIR)/$(APPLICATION_RUST_MODULE)/

	$(Q)# On cortex-m0 boards like airfy-beacon, the archive contains a
	$(Q)# bin/thumbv6m-none-eabi.o file; the directory must be present for
	$(Q)# ar to unpack it...
	$(Q)mkdir -p $(BINDIR)/$(APPLICATION_RUST_MODULE)/bin/
	$(Q)cd $(BINDIR)/$(APPLICATION_RUST_MODULE)/ && $(AR) x $<
	$(Q)# ... and move them back if any exist, careful to err if anything is duplicate
	$(Q)rmdir $(BINDIR)/$(APPLICATION_RUST_MODULE)/bin/ || (mv -n $(BINDIR)/$(APPLICATION_RUST_MODULE)/bin/* $(BINDIR)/$(APPLICATION_RUST_MODULE)/ && rmdir $(BINDIR)/$(APPLICATION_RUST_MODULE)/bin/)

FORCE:
.phony: FORCE
